////////////////////////////////////////////////////////////////////////////
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//  Portions Copyright (c) Kentaro Sekimoto All rights reserved.
//
////////////////////////////////////////////////////////////////////////////

#include "..\..\..\DeviceCode\Targets\Native\RX62N\DeviceCode\RX62N_EDMAC_lwip\RX62N_EDMAC_lwip.h"
#include "KSZ8041_lwip.h"

static BOOL PhyReset(void)
{
    UINT32 i, data;

    if (!RX62N_EDMAC_lwip_WritePhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, BMCR_RESET, MII_WR_TOUT))
        return( FALSE );

    HAL_Time_Sleep_MicroSeconds(3);
    for (i = 0; i < MII_WR_TOUT; i++) {
        if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, &data, MII_RD_TOUT))
            return( FALSE );
        if (!(data & BMCR_RESET))
            return true;
    }
    return false;
}

static BOOL PhyVerifyId(void)
{
    UINT32 id1,id2;

    if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_IDR1, &id1, MII_RD_TOUT))
        return false;
    if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_IDR2, &id2, MII_RD_TOUT))
        return false;
    if (((id1 << 16) | (id2 & 0xFFF0)) != KSZ8041_ID)
        return false;
    return true;
}

static BOOL PhyAutoNegotiate(void)
{
    UINT32 i, data;

    if (!RX62N_EDMAC_lwip_WritePhy(KSZ8041_DEF_ADDR, PHY_REG_BMCR, PHY_AUTO_NEG, MII_WR_TOUT))
        return false;
    for (i = 0; i < 10; i++) {
        HAL_Time_Sleep_MicroSeconds(1000000);
        RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMSR, &data, MII_RD_TOUT);
        if (data & BMSR_AUTO_DONE)
            return true;
    }
    return false;
}

static BOOL PhySetLinkSpeed (void )
{
    UINT32 data;
    BOOL full_duplex, mbit_100;

    if (!PhyAutoNegotiate()) {
        debug_printf("Warning: Ethernet PHY Link autonegotiation failure.\r\n");
        mbit_100 = true;
        full_duplex = true;
    } else {
        if (!RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_BMSR, &data, MII_RD_TOUT))
            return false;
        if (data & STS_FULL_DUP)
            full_duplex = true;
        else
            full_duplex = false;

        if (data & STS_10_MBIT)
            mbit_100 = false;
        else
            mbit_100 = true;
    }
    RX62N_EDMAC_lwip_SetLinkSpeed(mbit_100, full_duplex);
    return true;
}

BOOL ENET_PHY_lwip_init ( void )
{
    if (!PhyReset()) {
        debug_printf("Error: Phy reset\r\n");
        return false;
    }
    if (!PhyVerifyId()) {
        debug_printf("Error: Invalid Phy Id\r\n");
        return false;
    }
    if (!PhySetLinkSpeed()) {
        debug_printf("Error: Link speed failure\r\n");
        return false;
    }
    return true;
}

UINT32 ENET_PHY_lwip_get_MDC_Clk_Div(void)
{
    return 1;
}

BOOL ENET_PHY_lwip_get_MII_mode(void)
{
    return false;
}

BOOL ENET_PHY_lwip_get_link_status(void)
{
    UINT32 data;
    BOOL link_status = false;

    if (RX62N_EDMAC_lwip_ReadPhy(KSZ8041_DEF_ADDR, PHY_REG_STS, &data, MII_RD_TOUT))
        link_status = (data & STS_LINK_ON);
    return link_status;
}

BOOL ENET_PHY_lwip_set_link_speed(void)
{
    return PhySetLinkSpeed();
}
